package com.jhc.drone.communication.network;


import com.jhc.drone.communication.enums.ProtocolEnum;
import com.jhc.drone.communication.network.tcp.TcpClientInboundHandler;
import com.jhc.drone.communication.processor.MessageProcessorFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.timeout.ReadTimeoutHandler;
import lombok.extern.slf4j.Slf4j;

import javax.net.ssl.SSLException;
import java.io.File;
import java.util.Arrays;

/**
 * @author lcp
 * @date 2025年01月2025/1/20 10:29
 * @describe
 */
@Slf4j
public class TcpGateway extends Gateway {

    public static int PORT = 7901;
    public static int SESSION_RECYCLER_EXPIRE = 20;//10;
    public static int TCP_FRAME_FIXED_HEADER_LENGTH = 4;     // 4 bytes
    public static int TCP_FRAME_MAX_BODY_LENGTH = 20 * 1024; // 6K bytes

    public static SslContext sslContext = null;

    protected final EventLoopGroup __bossGroup4Netty = new NioEventLoopGroup(1);
    protected final EventLoopGroup __workerGroup4Netty = new NioEventLoopGroup();
    protected Channel __serverChannel4Netty = null;
    protected ServerBootstrap bootstrap = null;
    MessageProcessorFactory processorFactory = null;

    public TcpGateway(MessageProcessorFactory processorFactory) {
        this.processorFactory = processorFactory;
        this.protocol = ProtocolEnum.TCP;

    }

    @Override
    public void init() {
        initSsl();
        bootstrap = new ServerBootstrap()
                .group(__bossGroup4Netty, __workerGroup4Netty)
                .channel(NioServerSocketChannel.class)
                .childHandler(initChildChannelHandler(processorFactory));

        bootstrap.option(ChannelOption.SO_BACKLOG, 4096);
        bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
        bootstrap.childOption(ChannelOption.TCP_NODELAY, true);
        bootstrap.childOption(ChannelOption.SO_RCVBUF, 1024*1024);
    }

    @Override
    public void bind() throws Exception {
        ChannelFuture cf = bootstrap.bind(PORT).sync();
        if (cf.isSuccess()) {
            log.info("TCP服务绑定端口" + PORT + "成功  " + (isSsl() ? "(已开启SSL/TLS加密传输)" : ""));
            timerTask();
        } else {
            log.info("TCP服务绑定端口" + PORT + "失败 ");
        }

        __serverChannel4Netty = cf.channel();
        __serverChannel4Netty.closeFuture().addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                __bossGroup4Netty.shutdownGracefully();
                __workerGroup4Netty.shutdownGracefully();
            }
        });

        log.info("TCP服务正在端口" + PORT + "上监听中...");
    }

    @Override
    public void shutdown() {
        if (__serverChannel4Netty != null)
            __serverChannel4Netty.close();
    }

    protected ChannelHandler initChildChannelHandler(final MessageProcessorFactory processorFactory) {
        return new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel channel) throws Exception {
                ChannelPipeline pipeline = channel.pipeline();
                if (isSsl()) {
                    pipeline.addFirst(sslContext.newHandler(channel.alloc()));
                }
                pipeline.addLast(new ReadTimeoutHandler(SESSION_RECYCLER_EXPIRE));
                pipeline.addLast(new TcpClientInboundHandler(processorFactory, protocol));
            }
        };
    }

    public  boolean isSsl() {
        return sslContext != null;
    }

    public void initSsl()  {
        try {
            // 加载服务器证书和私钥
            File certFile = new File("/home/ca/server.crt");
            File keyFile = new File("/home/ca/server.key");
            File caFile = new File("/home/ca/ca.crt");

            sslContext = SslContextBuilder.forServer(certFile, keyFile)
                    .trustManager(caFile)
                    .clientAuth(ClientAuth.REQUIRE) // 启用双向认证
                    .protocols("TLSv1.3")
                    .ciphers(Arrays.asList(
                            "TLS_AES_128_GCM_SHA256",
                            "TLS_AES_256_GCM_SHA384"
                    )) // 指定加密套件
                    .build();
        }catch (SSLException e) {
            log.error("init ssl failed{}" , e.getMessage());
        }
    }
}